﻿using OpenCvSharp;
using System;
using System.Text.RegularExpressions;

namespace FLANN
{
    public class Template
    {
        public Tuple<int, int, double, double> RotatingTemplateMatching(Mat input_src, Mat input_model,double angle_range, double angle_step,
            double angle_start, int num_levels, double score_threshold, int method)
        {
            double step = angle_step;
            double start = angle_start;
            double range = angle_range;
            
            //  定义匹配参数
            int result_cols = input_src.Cols - input_model.Cols + 1;
            int result_rows = input_src.Rows - input_model.Rows + 1;
            Mat result = new Mat(result_rows, result_cols, MatType.CV_8U);
            Mat src = new Mat();
            Mat model = new Mat();
            input_src.CopyTo(src);
            input_model.CopyTo(model);
            
            // 采取下采样提升运算速度
            for (int i = 0; i < num_levels; i++)
            {
                Cv2.PyrDown(src,src,new Size(src.Cols/2,src.Rows/2));
                Cv2.PyrDown(model,model,new Size(model.Cols/2,model.Rows/2));
            }

            TemplateMatchModes match_modes = TemplateMatchModes.CCoeffNormed;
            switch (method)
            {
                case 0:
                    match_modes = TemplateMatchModes.SqDiff;
                    break;
                case 
                    1:
                    match_modes = TemplateMatchModes.SqDiffNormed;
                    break;
                case 2:
                    match_modes = TemplateMatchModes.CCorr;
                    break;
                case 3:
                    match_modes = TemplateMatchModes.CCorrNormed;
                    break;
                case 4:
                    match_modes = TemplateMatchModes.CCoeff;
                    break;
                case 5:
                    match_modes = TemplateMatchModes.CCoeffNormed;
                    break;
            }
            
            // 在未经旋转进行第一次匹配
            Cv2.MatchTemplate(src,model,result,match_modes);
            Cv2.MinMaxLoc(result,out double min_val,out double max_val,out Point min_loc,out Point max_loc,new Mat());
            

            Point localtion = max_loc;
            double temp = max_val;
            double angle = 0;

            Mat new_image;
            
            for (int i = 0; i < (int) range / step; i++)
            {
                new_image = ImageRotate(model, start + step * i);
                Cv2.MatchTemplate(src,new_image,result,match_modes);
                Cv2.MinMaxLoc(result,out min_val,out max_val,out min_loc,out max_loc,new Mat());
                if (max_val > temp)
                {
                    localtion = max_loc;
                    temp = max_val;
                    angle = start + step * i;
                }
            }
            if (temp > score_threshold)
            {
                return new Tuple<int, int, double, double>
                ((int) (localtion.X * Math.Pow(2, num_levels)),(int)(localtion.Y * Math.Pow(2, num_levels)), angle, temp);
            }
            
            return new Tuple<int, int, double, double>(-1, -1, 0, 0);
        }

        static Mat ImageRotate(Mat image, double angle)
        {
            Mat new_image = new Mat();
            Point2f center = new Point2f((float)image.Cols/2,(float)image.Rows/2);
            Mat r = Cv2.GetRotationMatrix2D(center, angle, 1.0);
            Cv2.WarpAffine(image,new_image,r,image.Size());
            return new_image;
        }
    }
}